#include <os/irq.h>
#include <os/time.h>
#include <os/sched.h>
#include <os/string.h>
#include <os/stdio.h>
#include <os/smp.h>
#include <assert.h>
#include <sbi.h>
#include <screen.h>
#include <os/mm.h>
#include <pgtable.h>
#include <emacps/xemacps_example.h>
#include <plic.h>
handler_t irq_table[IRQC_COUNT];
handler_t exc_table[EXCC_COUNT];
uintptr_t riscv_dtb;

void reset_irq_timer()
{
    timer_check();
    sbi_set_timer(get_ticks() + TIMER_INTERVAL);
    do_scheduler();
}

void interrupt_helper(regs_context_t *regs, uint64_t stval, uint64_t cause)
{
    uint64_t mask = (1 << 63);
    if(cause >> 63)//scause   
        irq_table[cause & (~mask)](regs,stval,cause);
    else //SCAUSE_IRQ_FLAGæ˜?4'b1000000.....0
        exc_table[cause & (~mask)](regs,stval,cause);
}

void handle_int(regs_context_t *regs, uint64_t interrupt, uint64_t cause)
{
    reset_irq_timer();
}

//做一个模拟硬件虚实地址转换的函数。输入页表入口和虚拟地址，返回叶子的地址
uintptr_t check_page_helper(uintptr_t va, uintptr_t pgdir)
{
    uint64_t vpn[] = {(va >> 12) & ~(~0 << 9),
                      (va >> 21) & ~(~0 << 9),
                      (va >> 30) & ~(~0 << 9)};
    // Find PTE
    PTE *level_2 = (PTE *)pgdir + vpn[2];
    if (((*level_2) & 0x1) == 0)//检查valid位是否有效。无效则此虚拟地址未分配物理地址
        return 0;
    PTE *level_1 = (PTE *)pa2kva(((*level_2) >> 10) << 12) + vpn[1];
    if (((*level_1) & 0x1) == 0)
        return 0;
    PTE *final_pte = (PTE *)pa2kva(((*level_1) >> 10) << 12) + vpn[0];
    if (((*final_pte) & 0x1) == 0)
        return 0;
    return final_pte;
}
void handle_inst_page_fault(regs_context_t *regs, uint64_t stval, uint64_t cause)
{
    uintptr_t kva;
    if ((kva = check_page_helper(stval,
                pa2kva((uintptr_t)(current_running->pte_entry)))) != 0)
    {
        set_attribute(kva, (1 << 6) | (1 << 7));
        local_flush_tlb_all();
        return ;
    } else {
        printk("> Error: Inst page fault");
        while(1);
    }
}
void handle_load_page_fault(){

}
void handle_store_page_fault(regs_context_t *regs, uint64_t stval, uint64_t cause){
    //此类例外是向未进行映射的虚拟地址存储数据。stval是要写入的地址。处理的方法是分配一个物理页给该虚拟地址并在页表映射
    alloc_page_helper(stval,current_running->pte_entry);
}
extern uint64_t read_sip();

LONG ISR_judge(XEmacPs *InstancePtr)
{
    u32 RegISR = XEmacPs_ReadReg(InstancePtr->Config.BaseAddress,
                   XEMACPS_ISR_OFFSET);
    if ((RegISR & XEMACPS_IXR_FRAMERX_MASK) != 0x00000000U)
        return 1;
    return  0;
}
void handle_irq(regs_context_t *regs, int irq)//处理外部中断
{
    // Clear IXR and judge type
    int type = ISR_judge(&EmacPsInstance);//RX--1   TX--0
    if (ReadRXSR(&EmacPsInstance))
        ClearRXSR(&EmacPsInstance);
    if (ReadTXSR(&EmacPsInstance))
        ClearTXSR(&EmacPsInstance);
    if (type % 2 == 0 && !list_empty(&net_send_queue))
        do_unblock(net_send_queue.next);
    if (type && !list_empty(&net_recv_queue))
        do_unblock(net_recv_queue.next);
    // let PLIC know that handle_irq has been finished
    plic_irq_eoi(irq);
}
void init_exception()
{
    int i;
    for(i=0;i<IRQC_COUNT;i++)
        irq_table[i]=&handle_other;
    for(i=0;i<EXCC_COUNT;i++)
        exc_table[i]=&handle_other;
    irq_table[5]=&handle_int;
    irq_table[9] = &plic_handle_irq;
    //irq_table[9]=&handle_ext_int;//exception code 9 is external interrupty and 63th(interrupt) is 1
    exc_table[8]=&handle_syscall;
    exc_table[12]=&handle_inst_page_fault;
    exc_table[13]=&handle_load_page_fault;
    exc_table[15]=&handle_store_page_fault;
    setup_exception(&pid0_pcb);
}

void handle_other(regs_context_t *regs, uint64_t stval, uint64_t cause)
{
    uint64_t mhartid = get_current_cpu_id();
    char* reg_name[] = {
        "zero "," ra  "," sp  "," gp  "," tp  ",
        " t0  "," t1  "," t2  ","s0/fp"," s1  ",
        " a0  "," a1  "," a2  "," a3  "," a4  ",
        " a5  "," a6  "," a7  "," s2  "," s3  ",
        " s4  "," s5  "," s6  "," s7  "," s8  ",
        " s9  "," s10 "," s11 "," t3  "," t4  ",
        " t5  "," t6  "
    };
    for (int i = 0; i < 32; i += 3) {
        for (int j = 0; j < 3 && i + j < 32; ++j) {
            printk("%s : %016lx ",reg_name[i+j], regs->regs[i+j]);
        }
        printk("\n\r");
    }
    printk("sstatus: 0x%lx sbadaddr: 0x%lx scause: %lu\n\r",
           regs->sstatus, regs->sbadaddr, regs->scause);
    printk("sepc: 0x%lx pid: %d mhartid: %d\n\r", 
           regs->sepc, current_running->pid, mhartid);

    // if (regs->sbadaddr != 0) {
    //     PTE* pte = va2pte(regs->sbadaddr, current_running->pgdir);
    //     if (pte != NULL) {
    //         printk("PTE : %lx %lx\n\r", (uintptr_t) pte, *pte);
    //         uintptr_t baseAddr = ((uintptr_t) pte) >> 12 << 12;
    //         for (uintptr_t addr = baseAddr; addr < baseAddr + PAGE_SIZE; addr += 8) {
    //             printk("PTE : %lx %lx %lx\n\r", addr, get_pfn(*(PTE*)addr), get_attribute(*(PTE*)addr, 0x3ff));
    //         }
    //     }
    // }

    uintptr_t fp = regs->regs[8], sp = regs->regs[2];
    printk("[Backtrace]\n\r");
    printk("  addr: %lx sp: %lx fp: %lx\n\r", regs->regs[1] - 4, sp, fp);
    // while (fp < USER_STACK_ADDR && fp > USER_STACK_ADDR - PAGE_SIZE) {
    while (fp < USER_STACK_ADDR && fp > 0x10000) {
        uintptr_t prev_ra = *(uintptr_t*)(fp-8);
        uintptr_t prev_fp = *(uintptr_t*)(fp-16);

        printk("  addr: %lx sp: %lx fp: %lx\n\r", prev_ra - 4, fp, prev_fp);

        fp = prev_fp;
    }
    assert(0);
}